package cn.iocoder.yudao.module.pay.service.point;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.controller.app.point.vo.AppPayPointRechargeCreateReqVO;
import cn.iocoder.yudao.module.pay.controller.app.point.vo.AppPayPointRechargePageReqVO;
import cn.iocoder.yudao.module.pay.controller.app.point.vo.AppPayPointRechargeSaveReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.point.PayPointRechargePackageDO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.framework.pay.config.PayPointProperties;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import cn.iocoder.yudao.module.pay.controller.admin.point.vo.*;
import cn.iocoder.yudao.module.pay.dal.dataobject.point.PayPointRechargeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.pay.dal.mysql.point.PayPointRechargeMapper;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

import static cn.hutool.core.util.ObjectUtil.notEqual;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr;
import static cn.iocoder.yudao.module.pay.convert.point.PayPointRechargeConvert.INSTANCE;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.WXA_WALLET_RECHARGER_PAID;

/**
 * 会员钻石充值 Service 实现类
 *
 * @author Jerry
 */
@Service
@Slf4j
public class PayPointRechargeServiceImpl implements PayPointRechargeService {

    @Resource
    private PayPointRechargeMapper pointRechargeMapper;

    @Resource
    private PayPointRechargePackageService payPointRechargePackageService;

    @Resource
    private PayOrderService payOrderService;

    @Resource
    private PayPointProperties payPointProperties;

    @Resource
    public SocialClientApi socialClientApi;

    @Resource
    private MemberPointApi memberPointApi;

    @Resource
    private MemberLevelApi memberLevelApi;

    @Resource
    private MemberUserApi memberUserApi;

    private static final String WALLET_RECHARGE_ORDER_SUBJECT = "积分余额充值";

    @Override
    public Long createPointRecharge(PayPointRechargeSaveReqVO createReqVO) {
        // 插入
        PayPointRechargeDO pointRecharge = BeanUtils.toBean(createReqVO, PayPointRechargeDO.class);
        pointRechargeMapper.insert(pointRecharge);
        // 返回
        return pointRecharge.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayPointRechargeDO createPointRecharge(Long userId, Integer userType, String userIp, AppPayPointRechargeCreateReqVO reqVO) {
        // 1.1 计算充值金额
        int payPrice;
        int bonusPrice = 0;
        if (Objects.nonNull(reqVO.getPackageId())) {
            PayPointRechargePackageDO rechargePackage = payPointRechargePackageService.validPointRechargePackage(reqVO.getPackageId());
            payPrice = rechargePackage.getPayPrice();
            bonusPrice = rechargePackage.getBonusPrice();
        } else {
            payPrice = reqVO.getPayPrice();
        }
        // 1.2 插入充值记录
        PayPointRechargeDO recharge = INSTANCE.convert(userId, payPrice, bonusPrice, reqVO.getPackageId());
        pointRechargeMapper.insert(recharge);

        // 2.1 创建支付单
        Long payOrderId = payOrderService.createOrder(new PayOrderCreateReqDTO()
                .setAppKey(payPointProperties.getPointPayAppKey()).setUserIp(userIp)
                .setMerchantOrderId(recharge.getId().toString()) // 业务的订单编号
                .setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("")
                .setPrice(recharge.getPayPrice())
                .setExpireTime(addTime(Duration.ofHours(2L)))); // TODO @芋艿：支付超时时间
        // 2.2 更新钱包充值记录中支付订单
        pointRechargeMapper.updateById(new PayPointRechargeDO().setId(recharge.getId()).setPayOrderId(payOrderId));
        recharge.setPayOrderId(payOrderId);
        return recharge;
    }

    @Override
    public void updatePointRecharge(PayPointRechargeSaveReqVO updateReqVO) {
        // 校验存在
        validatePointRechargeExists(updateReqVO.getId());
        // 更新
        PayPointRechargeDO updateObj = BeanUtils.toBean(updateReqVO, PayPointRechargeDO.class);
        pointRechargeMapper.updateById(updateObj);
    }

    @Override
    public void deletePointRecharge(Long id) {
        // 校验存在
        validatePointRechargeExists(id);
        // 删除
        pointRechargeMapper.deleteById(id);
    }

    private void validatePointRechargeExists(Long id) {
        if (pointRechargeMapper.selectById(id) == null) {
            throw exception(POINT_RECHARGE_NOT_EXISTS);
        }
    }

    @Override
    public PayPointRechargeDO getPointRecharge(Long id) {
        return pointRechargeMapper.selectById(id);
    }

    @Override
    public PageResult<PayPointRechargeDO> getPointRechargePage(PayPointRechargePageReqVO pageReqVO) {
        return pointRechargeMapper.selectPage(pageReqVO);
    }

    @Override
    public Long createPointRecharge(AppPayPointRechargeSaveReqVO createReqVO) {
        // 插入
        PayPointRechargeDO pointRecharge = BeanUtils.toBean(createReqVO, PayPointRechargeDO.class);
        pointRechargeMapper.insert(pointRecharge);
        // 返回
        return pointRecharge.getId();
    }

    @Override
    public void updatePointRecharge(AppPayPointRechargeSaveReqVO updateReqVO) {
        // 校验存在
        validatePointRechargeExists(updateReqVO.getId());
        // 更新
        PayPointRechargeDO updateObj = BeanUtils.toBean(updateReqVO, PayPointRechargeDO.class);
        pointRechargeMapper.updateById(updateObj);
    }

    @Override
    public PageResult<PayPointRechargeDO> getPointRechargePage(AppPayPointRechargePageReqVO pageReqVO) {
        return pointRechargeMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<PayPointRechargeDO> getPointRechargePage(Long userId, Integer userType, PageParam pageReqVO, Boolean payStatus) {
        return pointRechargeMapper.selectPage(pageReqVO, userId, payStatus);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePointRechargerPaid(Long id, Long payOrderId) {
        // 1.1 获取钱包充值记录
        PayPointRechargeDO pointRecharge = pointRechargeMapper.selectById(id);
        if (pointRecharge == null) {
            log.error("[updateWalletRechargerPaid][钱包充值记录不存在，钱包充值记录 id({})]", id);
            throw exception(WALLET_RECHARGE_NOT_FOUND);
        }
        // 1.2 校验钱包充值是否可以支付
        PayOrderDO payOrderDO = validatePointRechargerCanPaid(pointRecharge, payOrderId);

        // 2. 更新钱包充值的支付状态
        int updateCount = pointRechargeMapper.updateByIdAndPaid(id, false,
                new PayPointRechargeDO().setId(id).setPayStatus(true).setPayTime(LocalDateTime.now())
                        .setPayChannelCode(payOrderDO.getChannelCode()));
        if (updateCount == 0) {
            throw exception(WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID);
        }

        // 3. 更新钱包余额
        memberPointApi.addPoint(pointRecharge.getUserId(), pointRecharge.getTotalPrice(), MemberPointBizTypeEnum.RECHARGE.getType(), String.valueOf(id));

        // TODO 根据匹配金额，开通会员
        if(pointRecharge.getTotalPrice().intValue() == 10 || pointRecharge.getTotalPrice().intValue() == 4000 || pointRecharge.getTotalPrice().intValue() == 8000 || pointRecharge.getTotalPrice().intValue() == 24000){
            memberPointApi.reducePoint(pointRecharge.getUserId(), pointRecharge.getTotalPrice(), MemberPointBizTypeEnum.VIP_USE.getType(), String.valueOf(id));
            Integer day = 7;
            if(pointRecharge.getTotalPrice().intValue() == 4000){
                day = 7;
            }else if(pointRecharge.getTotalPrice().intValue() == 8000){
                day = 31;
            }else if(pointRecharge.getTotalPrice().intValue() == 24000){
                day = 31*6;
            }
            memberUserApi.updateUserVipEndTime(pointRecharge.getUserId(), day);
        }

        // 增加用户经验
        memberLevelApi.addExperience(pointRecharge.getUserId(), pointRecharge.getTotalPrice(),
                MemberExperienceBizTypeEnum.ORDER_GIVE.getType(), String.valueOf(id));

        // 4. 发送订阅消息
        getSelf().sendPointRechargerPaidMessage(payOrderId, pointRecharge);
    }

    @Async
    public void sendPointRechargerPaidMessage(Long payOrderId, PayPointRechargeDO pointRecharge) {
        // 2. 构建并发送模版消息
        socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO()
                .setUserId(pointRecharge.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
                .setTemplateTitle(WXA_WALLET_RECHARGER_PAID)
                .setPage("pages/user/wallet/money") // 钱包详情界面
                .addMessage("character_string1", String.valueOf(payOrderId)) // 支付单编号
                .addMessage("amount2", fenToYuanStr(pointRecharge.getTotalPrice())) // 充值金额
                .addMessage("time3", LocalDateTimeUtil.formatNormal(pointRecharge.getCreateTime())) // 充值时间
                .addMessage("phrase4", "充值成功")); // 充值状态
    }

    private PayOrderDO validatePointRechargerCanPaid(PayPointRechargeDO pointRecharge, Long payOrderId) {
        // 1.1 校验充值记录的支付状态
        if (pointRecharge.getPayStatus()) {
            log.error("[validatePointRechargerCanPaid][钱包({}) 不处于未支付状态!  钱包数据是：{}]",
                    pointRecharge.getId(), toJsonString(pointRecharge));
            throw exception(WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID);
        }
        // 1.2 校验支付订单匹配
        if (notEqual(pointRecharge.getPayOrderId(), payOrderId)) { // 支付单号
            log.error("[validatePointRechargerCanPaid][钱包({}) 支付单不匹配({})，请进行处理！ 钱包数据是：{}]",
                    pointRecharge.getId(), payOrderId, toJsonString(pointRecharge));
            throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR);
        }

        // 2.1 校验支付单是否存在
        PayOrderDO payOrder = payOrderService.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validatePointRechargerCanPaid][钱包({}) payOrder({}) 不存在，请进行处理！]",
                    pointRecharge.getId(), payOrderId);
            throw exception(PAY_ORDER_NOT_FOUND);
        }
        // 2.2 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validatePointRechargerCanPaid][钱包({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    pointRecharge.getId(), payOrderId, toJsonString(payOrder));
            throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 2.3 校验支付金额一致
        if (notEqual(payOrder.getPrice(), pointRecharge.getPayPrice())) {
            log.error("[validateDemoOrderCanPaid][钱包({}) payOrder({}) 支付金额不匹配，请进行处理！钱包 数据是：{}，payOrder 数据是：{}]",
                    pointRecharge.getId(), payOrderId, toJsonString(pointRecharge), toJsonString(payOrder));
            throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH);
        }
        // 2.4 校验支付订单的商户订单匹配
        if (notEqual(payOrder.getMerchantOrderId(), pointRecharge.getId().toString())) {
            log.error("[validateDemoOrderCanPaid][钱包({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    pointRecharge.getId(), payOrderId, toJsonString(payOrder));
            throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR);
        }
        return payOrder;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private PayPointRechargeServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}